#!/home/wcaarls/compile/bin/python

import sys, string, re, math

grid = [2.0, 2.0]

def link(src, dst, static_l, bidirectional=False):
  l = static_l[0]
  indent = "    "
  ret = ""
  ret += indent + "<link" + str(l) + ">\n"
  ret += indent + "  <E source=\"" + src + "\"/>\n"
  ret += indent + "  <E destination=\"" + dst + "\"/>\n"
  ret += indent + "</link" + str(l) + ">\n"
  l = l + 1
  if bidirectional:
    ret += indent + "<link" + str(l) + ">\n"
    ret += indent + "  <E source=\"" + dst + "\"/>\n"
    ret += indent + "  <E destination=\"" + src + "\"/>\n"
    ret += indent + "</link" + str(l) + ">\n"
    l = l + 1
  static_l[0] = l
  return ret

def node(name, bandwidth, pos=None, energy=None):
  ret = ""
  ret += "    <" + str(name) + ">\n"
  ret += "      <E derives=\"fabric\"/>\n"
  ret += "      <E address=\"*\"/>\n"
  if pos:
    ret += "      <E pos=\"" + pos + "\"/>\n"
    ret += "      <E pos=\"" + pos + "\"/>\n"
  ret += "      <env operation=\"transport\">\n"
  ret += "        <E time=\"" + str(1.0/bandwidth) + "\"/>\n"
  if energy:
    ret += "        <E energy=\"" + str(energy) + "\"/>\n"
  ret += "      </env>\n"
  ret += "    </" + str(name) + ">\n"
  return ret

class Mapper:
    def __init__(self, type, delay, maxtime, avgtime, avgsqtime, avgenergy, avgresources):
      self.type = str(type)
      self.evaluator = "dependent"
      self.delay = int(delay)
      self.maxtime = float(maxtime)
      self.avgtime = float(avgtime)
      self.avgsqtime = float(avgsqtime)
      self.avgenergy = float(avgenergy)
      self.avgresources = float(avgresources)
    def __repr__(self):
      return "{%s, %s, %d, %f, %f, %f, %f, %f}" % (self.type, self.evaluator, self.delay, self.maxtime, self.avgtime, self.avgsqtime, self.avgenergy, self.avgresources)
    def GenerateXML(self):
      ret =  "  <!-- mapper description generated from:\n"
      ret += "       " + repr(self) + "\n"
      ret += "    -->\n\n"
      ret +=  "<mapper>\n"
      ret += "  <E type=\"%s\"/>\n" % (self.type)
      ret += "  <E evaluator=\"%s\"/>\n" % (self.evaluator)
      ret += "  <E delay=\"%d\"/>\n" % (self.delay)
      ret += "  <E maxtime=\"%f\"/>\n" % (self.maxtime)
      ret += "  <E avgtime=\"%f\"/>\n" % (self.avgtime)
      ret += "  <E avgsqtime=\"%f\"/>\n" % (self.avgsqtime)
      ret += "  <E avgenergy=\"%f\"/>\n" % (self.avgenergy)
      ret += "  <E avgresources=\"%f\"/>\n" % (self.avgresources)
      ret += "</mapper>\n"
      return ret

class Component:
    def __init__(self, type, freq, mem, bandwidth=None):
        self.type = type
        self.freq = float(freq)
        self.mem = int(mem)
        if bandwidth:
          self.bandwidth = float(bandwidth)
        else:
          self.bandwidth = None
        self.id = 0
    def __str__(self):
        return str(self.type) + "-" + str(self.id)
    def __repr__(self):
        return "{" + str(self.type) + ", " + str(self.freq) + ", " + str(self.mem) + ", " + str(self.bandwidth) + "}"    
    def GenerateXML(self):
        ret = ""
        ret += "    <" + str(self) + ">\n"
        ret += "      <E derives=\"" + str(self.type) + "\"/>\n"
        ret += "      <E address=\"/" + str(self.type) + "/" + str(self.id) + "\"/>\n"
        ret += "      <E frequency=\"" + str(self.freq) + "\"/>\n"
        ret += "      <env resource = \"memory\">\n"
        ret += "        <E count=\"" + str(self.mem) + "\"/>\n"
        ret += "      </env>\n"
        ret += "    </" + str(self) + ">\n"
        return ret

class Architecture:
    def __init__(self, type, bandwidth):
        self.type = type
        self.bandwidth = float(bandwidth)
        self.components = []
    def __repr__(self):
        return "{" + str(self.type) + ", " + str(self.bandwidth) + ", " + str(self.components) + "}"
    def __eq__(self, other):
        try:
            return self.__repr__() == other.__repr__()
        except:
            return False
        
    def __hash__(self):
        return self.__repr__().__hash__()

    def __ne__(self, other):
        return not (self == other)      

    def AddComponent(self, component):
        if not component.bandwidth:
          component.bandwidth = self.bandwidth
        component.id = len(self.components)
        self.components.append(component)
    def GenerateXML(self, base=None):
        linkid = [0]
        
        # S, mm, fJ
        ee_freq       = 100000000
        ee_wires      = math.ceil(self.bandwidth/ee_freq)
        ee_components = len(self.components)
        ee_carea      = 30
        ee_lambda     = 0.00009
        ee_wenergy    = 0.5 * 670000 * 1.8*1.8
        ee_busenergy  = ee_wires * 3 * ee_lambda * ee_components * math.sqrt(ee_carea) * ee_wenergy
        ee_ringenergy = ee_wires * (3 * ee_lambda * math.sqrt(ee_carea) * ee_wenergy)

        # J / byte
        ee_busenergy = ee_busenergy * pow(10, -15) * 8
        ee_ringenergy = ee_ringenergy * pow(10, -15) * 8

        ret = "<document>\n\n"

        if base != None:
            ret += "  <!-- include " + base + " -->\n\n"

            f = open(base, "r")
            for l in f.readlines():
                ret += l
            
            f.close()

            ret += "\n  <!-- end include " + base + " -->\n\n"

        ret += "  <!-- architecture model generated from:\n"
        ret += "       " + repr(self) + "\n"
        ret += "    -->\n\n"
        
        ret += "  <targets>\n"
        for c in self.components:
            ret += c.GenerateXML()
            
            ret += node(str(c) + "-in", c.bandwidth)
            
            # Model energy at outgoing port
            if self.type == "bus" or self.type == "fc":
              ret += node(str(c) + "-out", c.bandwidth, None, ee_busenergy)
            else:
              ret += node(str(c) + "-out", c.bandwidth)

            if self.type == "ring" or self.type == "uniring":
                ret += node(str(c) + "-node", float('inf'))
                ret += node(str(c) + "-node-to-" + str(self.components[(c.id+1)%len(self.components)]) + "-node", self.bandwidth, None, ee_ringenergy)

        if self.type == "torus" or self.type == "mesh":
            # Determine torus width and height. Try to keep it as square as possible
            N = len(self.components)
            X = int(math.floor(math.sqrt(N)))
            Y = int(math.ceil(math.sqrt(N)))
            if N > X*Y:
              X = X + 1
                
            for x in range(X):
              for y in range(Y):
                if y*X+x < N:
                  c = self.components[y*X+x]
                  ret += "    <" + str(c) + ">\n"
                  ret += "      <E pos=\"%f,%f\"/>\n" % ((x+0.5)*grid[0], (y+0.5)*grid[1])
                  ret += "    </" + str(c) + ">\n"
                  ret += "    <" + str(c) + "-in>\n"
                  ret += "      <E pos=\"%f,%f\"/>\n" % ((x+.1875)*grid[0], (y+.3125)*grid[1])
                  ret += "    </" + str(c) + "-in>\n"
                  ret += "    <" + str(c) + "-out>\n"
                  ret += "      <E pos=\"%f,%f\"/>\n" % ((x+.3125)*grid[0], (y+.1875)*grid[1])
                  ret += "    </" + str(c) + "-out>\n"
                  
                ret += node("node" + str(x) + str(y), float('inf'), str(x*grid[0]) + ", " + str(y*grid[1]))

                if self.type == "torus" or x < X-1:                
                  ret += node("node" + str(x) + str(y) + "-to-node" + str((x+1)%X) + str(y), self.bandwidth, str((x+0.5)*grid[0]) + ", " + str(y*grid[1]))

                if self.type == "torus" or y < Y-1:                
                  ret += node("node" + str(x) + str(y) + "-to-node" + str(x) + str((y+1)%Y), self.bandwidth, str(x*grid[0]) + ", " + str((y+0.5)*grid[1]))
                  
        if self.type == "bus":
           ret += node("bus", self.bandwidth, None, )

        ret += "  </targets>\n"
        ret += "  <links>\n"

        for c in self.components:
            ret += link(str(c) + "-in", str(c), linkid)
            ret += link(str(c), str(c) + "-out", linkid)
        
            if self.type == "fc":
                for d in self.components:
                    if c != d:
                        ret += link(str(c)+"-out", str(d) + "-in", linkid)
                        
            if self.type == "ring" or self.type == "uniring":
                ret += link(str(c) + "-node", str(c) + "-in", linkid)
                ret += link(str(c) + "-out", str(c) + "-node", linkid)
                
                ret += link(str(self.components[(c.id-1)%len(self.components)]) + "-node-to-" + str(c) + "-node", str(c) + "-node", linkid)
                ret += link(str(c) + "-node", str(c) + "-node-to-" + str(self.components[(c.id+1)%len(self.components)]) + "-node", linkid)

            if self.type == "ring":
                ret += link(str(c) + "-node", str(self.components[(c.id-1)%len(self.components)]) + "-node-to-" + str(c) + "-node", linkid)
                ret += link(str(c) + "-node-to-" + str(self.components[(c.id+1)%len(self.components)]) + "-node", str(c) + "-node", linkid)

            if self.type == "bus":
                ret += link("bus", str(c) + "-in", linkid)
                ret += link(str(c) + "-out", "bus", linkid)
        
        if self.type == "torus" or self.type == "mesh":
            for x in range(X):
                for y in range(Y):
                    ret += "    <!-- (" + str(x) + ", " + str(y) + ") -->\n"
                    if y*X+x < N:
                        ret += link("node" + str(x) + str(y), str(self.components[y*X+x]) + "-in", linkid)
                        ret += link(str(self.components[y*X+x]) + "-out", "node" + str(x) + str(y), linkid)
                        
                    if self.type == "torus" or x < X-1:                        
                      ret += link("node"+str(x)+str(y), "node" + str(x)+str(y)+"-to-node" + str((x+1)%X)+str(y), linkid)
                      ret += link("node" + str(x)+str(y)+"-to-node" + str((x+1)%X)+str(y), "node"+str(x)+str(y), linkid)

                    if self.type == "torus" or y < Y-1:
                      ret += link("node"+str(x)+str(y), "node" + str(x)+str(y)+"-to-node" + str(x)+str((y+1)%Y), linkid)
                      ret += link("node" + str(x)+str(y)+"-to-node" + str(x)+str((y+1)%Y), "node"+str(x)+str(y), linkid)

                    if self.type == "torus" or x > 0:
                      ret += link("node"+str(x)+str(y), "node" + str((x-1)%X)+str(y)+"-to-node" + str(x)+str(y), linkid)
                      ret += link("node" + str((x-1)%X)+str(y)+"-to-node" + str(x)+str(y), "node"+str(x)+str(y), linkid)
                    
                    if self.type == "torus" or y > 0:
                      ret += link("node"+str(x)+str(y), "node" + str(x)+str((y-1)%Y)+"-to-node" + str(x)+str(y), linkid)
                      ret += link("node" + str(x)+str((y-1)%Y)+"-to-node" + str(x)+str(y), "node"+str(x)+str(y), linkid)

        ret += "  </links>\n"
        ret += "</document>\n"
        return ret

class System:
    def __init__(self, architecture, mapper):
      self.architecture = architecture
      self.mapper = mapper
    def __repr__(self):
      return "{" + str(self.architecture) + ", " + str(self.mapper) + "}"
      
    def __eq__(self, other):
        try:
            return self.__repr__() == other.__repr__()
        except:
            return False
        
    def __hash__(self):
        return self.__repr__().__hash__()

    def __ne__(self, other):
        return not (self == other)      

    def GenerateXML(self, base=None):
      ret = "<document>\n"
      ret += self.architecture.GenerateXML(base)
      ret += self.mapper.GenerateXML()
      ret += "</document>\n"
      return ret
      
def ReadSystem(line, cre=re.compile("\{\{(.*), (.*), \[(.*)\]\}, \{(.*), (.*), (.*), (.*), (.*), (.*), (.*), (.*)\}\}")):
    m = cre.match(line)
    a = Architecture(m.group(1), m.group(2))
    map = Mapper(m.group(4), m.group(6), m.group(7), m.group(8), m.group(9), m.group(10), m.group(11))
  
    cs = m.group(3)
    cs = cs.replace("[", " ")
    cs = cs.replace("]", " ")
    cs = cs.replace("{", " ")
    cs = cs.replace("}", " ")
    cs = cs.replace(",", " ")
    cs = cs.split()
    
    for i in range(0, len(cs), 4):
      a.AddComponent(Component(cs[i], cs[i+1], cs[i+2], cs[i+3]))

    return System(a, map)

def BuildSystem(argv):
    if (len(argv)-9)%4 == 0:
        mappersettings = 7
    else:
        mappersettings = 0

    a = Architecture(argv[0], argv[1])
    for c in range(2, len(argv)-mappersettings, 4):
        a.AddComponent(Component(argv[c], argv[c+1], argv[c+2], argv[c+3]))

    if mappersettings:
        m = Mapper(argv[-7], argv[-6], argv[-5], argv[-4],argv[-3],argv[-2],argv[-1])
    else:
        m = Mapper("greedy", 0, 1.0, 0.0, 0.0, 0.0, 0.0)

    return System(a, m)

if __name__ == "__main__":
    if len(sys.argv) < 4 or (((len(sys.argv)-4)%4 != 0) and ((len(sys.argv)-11)%4 != 0)):
        print >>sys.stderr, "SmartCam Architecture Model Builder"
        print >>sys.stderr, "Usage:"
        print >>sys.stderr, "  arch.py <master> <fc|torus|mesh|ring|uniring|bus> <bandwidth> [<arch> <freq> <mem> <bandwidth>] ..."
        print >>sys.stderr, "          ... [type delay maxtime avgtime avgsqtime avgenergy avgresources]"
        sys.exit()

    s = BuildSystem(sys.argv[2:])
    print s.GenerateXML(sys.argv[1])

    import simulate
    print "<!-- area: " + str(simulate.cost(s)) + " -->"
